package com.myk.game.gobangchess.rooms;

import com.myk.game.gobangchess.constants.ColorCost;
import com.myk.game.gobangchess.constants.WinMode;
import com.myk.game.gobangchess.rooms.absrooms.AbsOneTwoTwoTwoStepRoom;
import com.myk.game.gobangchess.signs.ColorSign;
import com.myk.game.gobangchess.signs.GroundSign;
import com.myk.game.gobangchess.signs.LineSign;
import com.myk.game.gobangchess.signs.TextSign;
import com.myk.game.gobangchess.utils.GridsUtils;

import java.util.*;

/**
 * 三格外围接棋
 */
public class GoLinkThreeOutRoom extends AbsOneTwoTwoTwoStepRoom {

    private DrawIndexRecord drawIndexRecord;
    private int[] firstPoint; // 记录第一个落子位置
    private static final int BOARD_SIZE = 17;
    private static final int MIN_DISTANCE = 3; // 最小曼哈顿距离
    private static final int BLACK = 1; // 黑方棋子
    private static final int WHITE = 2; // 白方棋子
    private List<Integer> goLinkIndexList; //连通的路径

    public GoLinkThreeOutRoom(String roomName) {
        super(roomName, "在围接棋基础上，采用六子棋的落子机制，但次落子的两个棋子得在三格之外。", "三格外围接棋" , GridsUtils.createEmptyGrids(BOARD_SIZE, BOARD_SIZE), true, true);
    }

    @Override
    public String getRuleText() {
        return "《三格外围接棋》\n" +
                "使用17×17格的棋盘\n" +
                "黑方先下一子，之后黑白双方轮流下两个棋子（黑棋和白棋）\n" +
                "落下两个棋子必须得在三格之外，无法落子判负\n" +
                "吃子与禁入规则均同围棋\n" +
                "先以直线两两相连的棋子连通两条对边的一方获胜";
    }

    /**
     * 获取游戏信息 - 用于前端显示和状态同步
     * 包含棋盘状态、颜色提示、游戏提示等完整信息
     * @return 格式化的游戏信息字符串
     */
    @Override
    public String getGameInfo() {
        signList.clear(); // 清空之前的标记

        // 添加棋盘边框
        signList.add(new GroundSign(getIndex(0, 0), getIndex(BOARD_SIZE - 1, BOARD_SIZE - 1), ColorCost.TRANSPARENT, ColorCost.BOARD_LINE, 1.5f, 0, 0));

        // 添加三格内不可落子位置提示（灰色半透明膜）
        if (firstPoint != null) {
            int firstRow = firstPoint[0];
            int firstCol = firstPoint[1];
            
            // 遍历整个棋盘，标记距离小于3的格子
            for (int row = 0; row < BOARD_SIZE; row++) {
                for (int col = 0; col < BOARD_SIZE; col++) {
                    if (curGrids[row][col] == 0) { // 空格子
                        int distance = Math.abs(row - firstRow) + Math.abs(col - firstCol);
                        if (distance <= MIN_DISTANCE) {
                            signList.add(new GroundSign(getIndex(row, col), ColorCost.GRAY_TRANSLUCENT, ColorCost.TRANSPARENT, 1f, 0, 0));
                        }
                    }
                }
            }
        }

        // 添加 最近落子提示
        if (drawIndexRecord != null){
            if (drawIndexRecord.firstIndex != -1){
                signList.add(new TextSign(drawIndexRecord.firstIndex, "※", ColorCost.RED_TEXT));
            }
            if (drawIndexRecord.secondIndex != -1){
                signList.add(new TextSign(drawIndexRecord.secondIndex, "※", ColorCost.RED_TEXT));
            }
        }

        //添加联通路径的提示
        if (goLinkIndexList != null){
            for (int i = 0; i < goLinkIndexList.size() - 1; i++) {
                signList.add(new LineSign(goLinkIndexList.get(i), goLinkIndexList.get(i + 1), ColorCost.BLUE, 1));
            }
        }

        // 添加游戏提示文字
        updateGameTip();

        // 返回标准格式的游戏信息
        return super.getGameInfo();
    }

    /**
     * 游戏准备回调方法 - 当两位玩家都准备好后，游戏最开始时调用
     * 负责初始化游戏状态
     */
    @Override
    protected void onStartPrepare() {
        drawIndexRecord = new DrawIndexRecord();
        updateGameTip(); // 更新游戏提示信息
        noticeBlackMove(); //通知黑方先行
    }

    /**
     * 重置棋盘方法 - 清空所有棋子
     * 通常在重新开始游戏时调用
     */
    @Override
    public void resetGrids() {
        super.resetGrids(); // 调用父类重置方法清空棋子
        drawIndexRecord = null;
        firstPoint = null; // 重置第一个落子位置
        secondPoint = null; // 重置第二个落子位置
        goLinkIndexList = null; // 清空联通路径
    }

    /**
     * 玩家落子处理主方法 - 处理玩家点击落子的逻辑
     * 根据三格外围接棋规则：
     * 1. 黑方和白方轮流落子（1222制：黑1→白2→黑2→白2...）
     * 2. 第二个棋子必须与第一个棋子距离≥3格（曼哈顿距离）
     * 3. 先连通两条对边的玩家获胜
     * 
     * @param row 落子的行坐标（0-16，左上角为0,0）
     * @param col 落子的列坐标（0-16，左上角为0,0）
     * @param value 落子方（1=黑方，2=白方）
     */
    @Override
    public void doRule(int row, int col, int value) {

        // 检查格子是否已被占用
        if (curGrids[row][col] != 0) {
            return;
        }
        
        // 检查距离限制规则（三格外）
        if (!canPlaceAtDistance(row, col)) {
            return;
        }
        
        // 检查自杀点（禁入点）
        if (isSuicideMove(row, col, value)) {
            return;
        }
        
        // 正确的1222落子制实现
        curGrids[row][col] = value;
        drawIndexRecord.record(value, getIndex(row, col));
        
        // 执行吃子操作
        int capturedCount = captureStones(row, col, value);
        if (capturedCount > 0) {
            noticeAllBroadcast("吃掉了" + capturedCount + "个棋子");
        }
        
        if (step == 0) {
            // 黑方第一步：落1子后立即切换
            stepAdd();
            noticeChangeMove();
        } else {
            // 1222规则：step > 0时，每回合落2子
            if (firstPoint == null) {
                // 每回合的第一个棋子
                firstPoint = new int[]{row, col};
                updateTempPointSigns();
                noticeAllRefreshGameInfo();
            } else {
                // 每回合的第二个棋子（必须与第一个棋子距离≥3格）
                secondPoint = new int[]{row, col};
                updateTempPointSigns();
                stepAdd();
                
                // 重置落子标记
                firstPoint = null;
                secondPoint = null;
                
                WinState winState = checkWin(row, col);
                if (winState != null) {
                    doOver(winState.winMode, winState.winMsg);
                } else {
                    // 落完2子后才切换玩家
                    noticeChangeMove();
                }
            }
        }
    }

    /**
     * 检查胜利条件 - 游戏的核心胜利判定逻辑
     * @param row 最新落子的行坐标
     * @param col 最新落子的列坐标
     * @return 胜利状态对象，包含胜利方和胜利原因，null表示游戏继续
     */
    @Override
    protected WinState checkWin(int row, int col) {
        // 检查连通对边的胜利条件：黑方或白方连通两条对边
        if (checkConnectOppositeSides(BLACK)) {
            return new WinState(WinMode.BLACK_WIN, "黑方连通两条对边获胜！");
        }
        if (checkConnectOppositeSides(WHITE)) {
            return new WinState(WinMode.WHITE_WIN, "白方连通两条对边获胜！");
        }

        // 检查无法落子的情况：当一方无合法落子位置时判负
        if (checkNoValidMoves()) {
            if (cur == 1) {
                return new WinState(WinMode.WHITE_WIN, "黑方无法落子，白方获胜！");
            } else {
                return new WinState(WinMode.BLACK_WIN, "白方无法落子，黑方获胜！");
            }
        }

        return null; // 游戏继续
    }

    private void updateGameTip() {
        if (cur == 0){
            gameTip = "-1";
            return;
        }
        if (step == 0) {
            gameTip = "黑方先手，请落一子到任意位置";
        } else if (firstPoint == null) {
            gameTip = (cur == 1 ? "黑方" : "白方") + "第一步，请落第一个棋子到任意位置";
        } else{
            gameTip = (cur == 1 ? "黑方" : "白方") + "第二步，请再落一子到三格之外的位置";
        }
    }
    
    /**
     * 检查指定位置是否可以落子（距离限制检查）
     * 
     * 规则：
     * - 黑方第一步(step=0)：无距离限制
     * - 每回合的第一个棋子：无距离限制
     * - 每回合的第二个棋子：必须与第一个棋子曼哈顿距离>3格
     * 
     * @param row 行坐标
     * @param col 列坐标
     * @return 是否可以落子
     */
    private boolean canPlaceAtDistance(int row, int col) {
        if (firstPoint == null) {
            return true; // 第一个棋子无限制
        }
        
        // 计算与第一个棋子的曼哈顿距离
        int distance = Math.abs(row - firstPoint[0]) + Math.abs(col - firstPoint[1]);
        return distance > MIN_DISTANCE;
    }
    
    /**
     * 创建当前棋盘的深度副本
     * 用于临时分析时不影响原棋盘状态
     * @return 当前棋盘的完整副本
     */
    private int[][] copyBoard() {
        int[][] copy = new int[BOARD_SIZE][BOARD_SIZE];
        for (int i = 0; i < BOARD_SIZE; i++) {
            for (int j = 0; j < BOARD_SIZE; j++) {
                copy[i][j] = curGrids[i][j];
            }
        }
        return copy;
    }

    /**
     * 检查是否为自杀点（禁入点）
     * 根据围棋规则，如果落子后会导致己方棋子无气，则为自杀点
     * 但如果落子后能吃掉对方棋子，则不算自杀点
     * @param row 落子行坐标
     * @param col 落子列坐标
     * @param player 落子玩家（1=黑方，2=白方）
     * @return true表示是自杀点，不能落子
     */
    private boolean isSuicideMove(int row, int col, int player) {
        int[][] tempBoard = copyBoard();
        tempBoard[row][col] = player;
        
        // 先检查是否能吃掉对方棋子
        int opponent = player == 1 ? 2 : 1;
        int capturedCount = 0;
        
        // 检查四个方向上的对方棋子
        int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
        
        for (int[] dir : directions) {
            int newRow = row + dir[0];
            int newCol = col + dir[1];
            
            if (newRow >= 0 && newRow < BOARD_SIZE && newCol >= 0 && newCol < BOARD_SIZE) {
                if (tempBoard[newRow][newCol] == opponent) {
                    boolean[][] visited = new boolean[BOARD_SIZE][BOARD_SIZE];
                    if (!hasLiberty(tempBoard, newRow, newCol, opponent, visited)) {
                        // 能吃掉对方棋子，不算自杀点
                        return false;
                    }
                }
            }
        }
        
        // 检查落子后己方棋子是否有气（只有在不能吃子的情况下）
        boolean[][] visited = new boolean[BOARD_SIZE][BOARD_SIZE];
        return !hasLiberty(tempBoard, row, col, player, visited);
    }
    
    /**
     * 检查指定位置的棋子是否有气（围棋规则）
     * @param board 当前棋盘状态
     * @param row 棋子行坐标
     * @param col 棋子列坐标
     * @param player 棋子所属玩家
     * @param visited 访问标记数组
     * @return true表示有气，false表示无气
     */
    private boolean hasLiberty(int[][] board, int row, int col, int player, boolean[][] visited) {
        if (row < 0 || row >= BOARD_SIZE || col < 0 || col >= BOARD_SIZE || visited[row][col]) {
            return false;
        }
        
        if (board[row][col] == 0) {
            return true; // 空位置，有气
        }
        
        if (board[row][col] != player) {
            return false; // 对方棋子，无气
        }
        
        visited[row][col] = true;
        
        // 检查上下左右四个方向
        boolean hasLib = hasLiberty(board, row-1, col, player, visited) ||
                         hasLiberty(board, row+1, col, player, visited) ||
                         hasLiberty(board, row, col-1, player, visited) ||
                         hasLiberty(board, row, col+1, player, visited);
        
        return hasLib;
    }
    
    /**
     * 检查并执行吃子操作（围棋规则）
     * 落子后检查周围对方棋子是否无气，无气的棋子被吃掉
     * @param row 最新落子的行坐标
     * @param col 最新落子的列坐标
     * @param player 落子玩家
     * @return 被吃掉的棋子数量
     */
    private int captureStones(int row, int col, int player) {
        int capturedCount = 0;
        int opponent = player == 1 ? 2 : 1;
        
        // 检查四个方向上的对方棋子
        int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
        
        for (int[] dir : directions) {
            int newRow = row + dir[0];
            int newCol = col + dir[1];
            
            if (newRow >= 0 && newRow < BOARD_SIZE && newCol >= 0 && newCol < BOARD_SIZE) {
                if (curGrids[newRow][newCol] == opponent) {
                    boolean[][] visited = new boolean[BOARD_SIZE][BOARD_SIZE];
                    if (!hasLiberty(curGrids, newRow, newCol, opponent, visited)) {
                        capturedCount += removeGroup(newRow, newCol, opponent);
                    }
                }
            }
        }
        
        return capturedCount;
    }
    
    /**
     * 移除无气的棋子组
     * @param row 棋子组起始行坐标
     * @param col 棋子组起始列坐标
     * @param player 棋子所属玩家
     * @return 被移除的棋子数量
     */
    private int removeGroup(int row, int col, int player) {
        if (row < 0 || row >= BOARD_SIZE || col < 0 || col >= BOARD_SIZE || curGrids[row][col] != player) {
            return 0;
        }
        
        curGrids[row][col] = 0; // 移除棋子
        int count = 1;
        
        // 递归移除相连的棋子
        count += removeGroup(row-1, col, player);
        count += removeGroup(row+1, col, player);
        count += removeGroup(row, col-1, player);
        count += removeGroup(row, col+1, player);
        
        return count;
    }
    
    /**
     * 检查指定玩家是否连通两条对边（胜利条件）
     * 如果连通，将连通路径的所有格子添加到goLinkIndexList中
     * @param player 玩家颜色（1=黑方，2=白方）
     * @return true表示该玩家已连通两条对边
     */
    private boolean checkConnectOppositeSides(int player) {
        goLinkIndexList = new ArrayList<>(); // 清空之前的路径
        
        // 分别检查上下边连通和左右边连通
        boolean topBottomConnected = checkTopBottomConnection(player);
        if (topBottomConnected) {
            return true;
        }
        
        boolean leftRightConnected = checkLeftRightConnection(player);
        if (leftRightConnected) {
            return true;
        }
        
        // 如果没有连通，清空路径列表
        goLinkIndexList.clear();
        return false;
    }
    
    /**
     * 检查上下两条对边的连通性
     * 如果连通，将连通路径的所有格子添加到goLinkIndexList中
     * @param player 玩家颜色
     * @return true表示该玩家的棋子连通上下边
     */
    private boolean checkTopBottomConnection(int player) {
        boolean[] topVisited = new boolean[BOARD_SIZE]; // 记录上边界棋子
        boolean[] bottomVisited = new boolean[BOARD_SIZE]; // 记录下边界棋子
        
        // 标记上边界（第0行）的该玩家棋子
        for (int col = 0; col < BOARD_SIZE; col++) {
            if (curGrids[0][col] == player) {
                topVisited[col] = true;
            }
        }
        
        // 标记下边界（第16行）的该玩家棋子
        for (int col = 0; col < BOARD_SIZE; col++) {
            if (curGrids[BOARD_SIZE-1][col] == player) {
                bottomVisited[col] = true;
            }
        }
        
        // 检查任意上边界棋子与任意下边界棋子的连通性
        for (int topCol = 0; topCol < BOARD_SIZE; topCol++) {
            if (topVisited[topCol]) {
                for (int bottomCol = 0; bottomCol < BOARD_SIZE; bottomCol++) {
                    if (bottomVisited[bottomCol]) {
                        List<Integer> path = findPath(0, topCol, BOARD_SIZE-1, bottomCol, player);
                        if (path != null && !path.isEmpty()) {
                            goLinkIndexList.addAll(path);
                            return true;
                        }
                    }
                }
            }
        }
        
        return false;
    }
    
    /**
     * 检查左右两条对边的连通性
     * 如果连通，将连通路径的所有格子添加到goLinkIndexList中
     * @param player 玩家颜色
     * @return true表示该玩家的棋子连通左右边
     */
    private boolean checkLeftRightConnection(int player) {
        boolean[] leftVisited = new boolean[BOARD_SIZE]; // 记录左边界棋子
        boolean[] rightVisited = new boolean[BOARD_SIZE]; // 记录右边界棋子
        
        // 标记左边界（第0列）的该玩家棋子
        for (int row = 0; row < BOARD_SIZE; row++) {
            if (curGrids[row][0] == player) {
                leftVisited[row] = true;
            }
        }
        
        // 标记右边界（第16列）的该玩家棋子
        for (int row = 0; row < BOARD_SIZE; row++) {
            if (curGrids[row][BOARD_SIZE-1] == player) {
                rightVisited[row] = true;
            }
        }
        
        // 检查任意左边界棋子与任意右边界棋子的连通性
        for (int leftRow = 0; leftRow < BOARD_SIZE; leftRow++) {
            if (leftVisited[leftRow]) {
                for (int rightRow = 0; rightRow < BOARD_SIZE; rightRow++) {
                    if (rightVisited[rightRow]) {
                        List<Integer> path = findPath(leftRow, 0, rightRow, BOARD_SIZE-1, player);
                        if (path != null && !path.isEmpty()) {
                            goLinkIndexList.addAll(path);
                            return true;
                        }
                    }
                }
            }
        }
        
        return false;
    }
    
    /**
     * 使用广度优先搜索查找两点之间的连通路径
     * @param startRow 起始行坐标
     * @param startCol 起始列坐标
     * @param endRow 目标行坐标
     * @param endCol 目标列坐标
     * @param player 玩家颜色
     * @return 连通路径的格子索引列表，如果无连通路径则返回null
     */
    private List<Integer> findPath(int startRow, int startCol, int endRow, int endCol, int player) {
        if (curGrids[startRow][startCol] != player) {
            return null;
        }
        
        boolean[][] visited = new boolean[BOARD_SIZE][BOARD_SIZE];
        Queue<int[]> queue = new LinkedList<>();
        Map<String, int[]> parent = new HashMap<>(); // 记录每个节点的父节点，用于路径重构
        
        // 起始点入队
        queue.offer(new int[]{startRow, startCol});
        visited[startRow][startCol] = true;
        
        int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; // 四个移动方向
        
        while (!queue.isEmpty()) {
            int[] current = queue.poll();
            int row = current[0];
            int col = current[1];
            
            // 到达目标点，重构路径
            if (row == endRow && col == endCol) {
                return reconstructPath(startRow, startCol, endRow, endCol, parent);
            }
            
            // 搜索四个方向的相邻格子
            for (int[] dir : directions) {
                int newRow = row + dir[0];
                int newCol = col + dir[1];
                
                // 检查边界和是否已访问，且必须是该玩家的棋子
                if (newRow >= 0 && newRow < BOARD_SIZE && newCol >= 0 && newCol < BOARD_SIZE &&
                    !visited[newRow][newCol] && curGrids[newRow][newCol] == player) {
                    visited[newRow][newCol] = true;
                    queue.offer(new int[]{newRow, newCol});
                    
                    // 记录父节点，用于路径重构
                    String key = newRow + "," + newCol;
                    String parentKey = row + "," + col;
                    parent.put(key, new int[]{row, col});
                }
            }
        }
        
        return null; // 未找到连通路径
    }
    
    /**
     * 重构从起点到终点的路径
     * @param startRow 起始行坐标
     * @param startCol 起始列坐标
     * @param endRow 目标行坐标
     * @param endCol 目标列坐标
     * @param parent 父节点映射
     * @return 路径的格子索引列表
     */
    private List<Integer> reconstructPath(int startRow, int startCol, int endRow, int endCol, Map<String, int[]> parent) {
        List<Integer> path = new ArrayList<>();
        
        // 从终点开始，沿着父节点回溯到起点
        int currentRow = endRow;
        int currentCol = endCol;
        
        while (true) {
            path.add(getIndex(currentRow, currentCol));
            
            // 如果到达起点，结束重构
            if (currentRow == startRow && currentCol == startCol) {
                break;
            }
            
            // 获取父节点
            String key = currentRow + "," + currentCol;
            int[] parentPos = parent.get(key);
            if (parentPos == null) {
                break; // 找不到父节点，结束重构
            }
            
            currentRow = parentPos[0];
            currentCol = parentPos[1];
        }
        
        // 反转路径，使其从起点到终点
        Collections.reverse(path);
        return path;
    }
    
    /**
     * 使用广度优先搜索检查两点之间的连通性
     * @param startRow 起始行坐标
     * @param startCol 起始列坐标
     * @param endRow 目标行坐标
     * @param endCol 目标列坐标
     * @param player 玩家颜色
     * @return true表示两点之间存在连通路径
     */
    private boolean isConnected(int startRow, int startCol, int endRow, int endCol, int player) {
        boolean[][] visited = new boolean[BOARD_SIZE][BOARD_SIZE];
        Queue<int[]> queue = new LinkedList<>();
        
        // 起始点必须是该玩家的棋子
        if (curGrids[startRow][startCol] != player) {
            return false;
        }
        
        // 广度优先搜索初始化
        queue.offer(new int[]{startRow, startCol});
        visited[startRow][startCol] = true;
        
        int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; // 四个移动方向
        
        while (!queue.isEmpty()) {
            int[] current = queue.poll();
            int row = current[0];
            int col = current[1];
            
            // 到达目标点
            if (row == endRow && col == endCol) {
                return true;
            }
            
            // 搜索四个方向的相邻格子
            for (int[] dir : directions) {
                int newRow = row + dir[0];
                int newCol = col + dir[1];
                
                // 检查边界和是否已访问，且必须是该玩家的棋子
                if (newRow >= 0 && newRow < BOARD_SIZE && newCol >= 0 && newCol < BOARD_SIZE &&
                    !visited[newRow][newCol] && curGrids[newRow][newCol] == player) {
                    visited[newRow][newCol] = true;
                    queue.offer(new int[]{newRow, newCol});
                }
            }
        }
        
        return false; // 未找到连通路径
    }
    
    /**
     * 检查当前玩家是否无合法落子位置
     * @return true表示无合法落子，游戏结束
     */
    private boolean checkNoValidMoves() {
        // 如果是第一个棋子，总是有合法落子位置（空格子）
        if (firstPoint == null) {
            // 检查是否有空格子
            for (int row = 0; row < BOARD_SIZE; row++) {
                for (int col = 0; col < BOARD_SIZE; col++) {
                    if (curGrids[row][col] == 0) {
                        return false; // 有空格子可落子
                    }
                }
            }
            return true; // 无空格子
        }
        
        // 第二个棋子，需要检查距离限制
        int firstRow = firstPoint[0];
        int firstCol = firstPoint[1];
        
        for (int row = 0; row < BOARD_SIZE; row++) {
            for (int col = 0; col < BOARD_SIZE; col++) {
                if (curGrids[row][col] == 0) { // 空格子
                    int distance = Math.abs(row - firstRow) + Math.abs(col - firstCol);
                    if (distance > MIN_DISTANCE) {
                        // 检查自杀规则
                        if (!isSuicideMove(row, col, cur)) {
                            return false; // 找到可落子位置
                        }
                    }
                }
            }
        }
        
        return true; // 无有效落子位置
    }

    public static class DrawIndexRecord{

        int side = 0;
        int firstIndex = -1;
        int secondIndex = -1;

        public void record(int side,int index){
            if (this.side != side){
                this.side = side;
                this.firstIndex = index;
                this.secondIndex = -1;
            }
            else {
                this.secondIndex = index;
            }
        }
    }
}
